package com.canbot.u05.versionupdate

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Handler
import android.os.Message
import android.text.TextUtils
import android.util.Log
import android.view.View
import com.alibaba.fastjson.JSONObject
import com.canbot.u05.versionupdate.utils.AppUtils
import com.canbot.u05.versionupdate.utils.EmailUtils
import com.canbot.u05.versionupdate.utils.FileUtils
import com.canbot.u05.versionupdate.utils.StringUtils
import com.coolerfall.download.DownloadCallback
import com.coolerfall.download.DownloadManager
import com.coolerfall.download.DownloadRequest
import com.example.bspatch.BsPatch
import okhttp3.*
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread

/**
 * Created by xh on 2017/6/2.
 */

class VersionUpdateUtils private constructor(private val mContext: Context) {

    private val mOkHttpClient by lazy {
        OkHttpClient.Builder()
                .addInterceptor(RetryInterceptor())
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .build()
    }

    private val mDownloadManager by lazy {
        DownloadManager.Builder().context(mContext).build()
    }

    private var mCall: Call? = null

    private lateinit var mHandler: Handler

    private var mCallback: VersionUpdateCallback? = null

    private var packageNames: String = ""

    private var zipMD5 = ""

    // 用于检测是否有更新或直接更新
    private var isStartUpdateVersion = false

    // 是否正在下载
    private var isDownloading = false

    private val apkFileInfos = ArrayList<ApkFileInfo>()

    /**
     * 是否有版本更新
     *
     * @param callback callback
     */
    fun isHasNewVersion(callback: VersionUpdateCallback) {
        // 本地端已开启更新
        if (UpdateConfig.CanUpgrade.not()) {
            Log.d(TAG, "本地版本更新已关闭")
            mCallback?.isHasNewVersion(isHas = false, enabledUpdate = false)
            return
        }
        Log.i(TAG, "开始检查版本更新")
        isStartUpdateVersion = false
        mCallback = callback
        checkVersion()
        // 通知头部检查版本
        // MsgSendUtils.sendStringMsg(MsgType.CHECK_NEW_VERSION, "check new version")
    }

    /**
     * 开始版本更新
     */
    fun startUpdateVersion(handler: Handler) {
        isStartUpdateVersion = true
        mCallback = null
        mHandler = handler
        if (TextUtils.isEmpty(packageNames)) {
            checkVersion()
        } else {
            checkVersionRequest(packageNames)
        }
    }

    private fun checkVersion() {
        val mac = UpdateConfig.PAD_MAC
        val headMac = UpdateConfig.HEAD_MAC
        val url = "${UpdateConfig.VERSION_UPDATE_URL}queryAllVersion/${UpdateConfig.PROJECT_NAME}/${UpdateConfig.ROBOT_TYPE}/${UpdateConfig.BRANCH_NAME}/${UpdateConfig.VERSION_CODE}/$mac/$headMac"

        Log.e(TAG, "获取服务器版本信息 url -> $url")

        val request = Request.Builder().url(url).build()

        mCall = mOkHttpClient.newCall(request)

        mCall!!.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                val msg = if (call.isCanceled) {
                    "请求被取消"
                } else {
                    val detailError = StringUtils.getErrorDetail(e)
                    mCallback?.onFailure(detailError)
                    "request failed -> $detailError"
                }
                Log.e(TAG, "$url $msg")
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                if (response.isSuccessful) {
                    val result = response.body()!!.string()
                    Log.e(TAG, "服务器版本信息 -> $result")
                    var serverApkInfos: List<Map<*, *>>? = null
                    try {
                        serverApkInfos = JSONObject.parseArray(result, Map::class.java)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        EmailUtils.sendUpdateErrorEmail(StringUtils.getErrorDetail(e))
                    }

                    if (serverApkInfos == null || serverApkInfos.isEmpty()) {
                        mCallback?.isHasNewVersion(false)
                        Log.e(TAG, "服务器上暂无版本信息或已关闭版本更新")
                    } else {
                        compareVersion(serverApkInfos)
                    }
                } else {
                    mCallback?.onFailure("getAllVersion failed response not successful")
                    Log.e(TAG, "getAllVersion onResponse Failure -> $response")
                }
                response.close()
            }
        })
    }

    /**
     * 比较版本号
     */
    private fun compareVersion(serverVersions: List<Map<*, *>>) {
        val needUpdateApkInfos = ArrayList<Map<String, String>>()
        for (serverVersion in serverVersions) {

            val packageName = serverVersion["packageName"] as String
            val versionCode = (serverVersion["versionCode"] as String).toInt()

            // ai 资源
            if (AI_PACKAGE_NAME == packageName) {
                var localAiVersionCode: Int
                try {
                    localAiVersionCode = UpdateConfig.aiVersionCode.toInt()
                } catch (e: NumberFormatException) {
                    e.printStackTrace()
                    EmailUtils.sendUpdateErrorEmail("本地 ai 版本号不是正整数，version = ${UpdateConfig.aiVersionCode}")
                    continue
                }

                serverAiVersionCode = versionCode
                if (localAiVersionCode < versionCode) {
                    val map = HashMap<String, String>()
                    map["packageName"] = packageName
                    map["versionCode"] = localAiVersionCode.toString()
                    map["oldApkMd5"] = ""
                    needUpdateApkInfos.add(map)
                }
                continue
            }

            // apk 是否已安装
            if (!RobotUtil.isPkgInstalled(mContext, packageName)) {
                val map = HashMap<String, String>()
                map["packageName"] = packageName
                map["versionCode"] = "-1"
                map["oldApkMd5"] = ""
                needUpdateApkInfos.add(map)
                Log.e(TAG, "$packageName 未安装，需要更新")
                continue
            }

            try {
                // 系统已经安装的 apk 的版本号.
                val clientApkVersionCode = mContext.packageManager.getPackageInfo(packageName, 0).versionCode
                // 服务器的版本号
                if (versionCode > clientApkVersionCode) {
                    val map = HashMap<String, String>()
                    map["packageName"] = packageName
                    map["versionCode"] = clientApkVersionCode.toString()
                    map["oldApkMd5"] = Md5Utils.encryptMD5File2String(File(RobotUtil.getSourceApkPath(mContext, packageName)!!))
                            ?: ""
                    needUpdateApkInfos.add(map)
                    Log.e(TAG, "$packageName 当前版本：$clientApkVersionCode < 服务器版本：$versionCode, 需要更新")
                } else {
                    Log.e(TAG, "$packageName 无需更新")
                }
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
            }
        }

        if (needUpdateApkInfos.size == 0) {
            mCallback?.isHasNewVersion(false)
            Log.e(TAG, "当前已经是最新版本")
        } else {
            packageNames = JSONObject.toJSONString(needUpdateApkInfos)
            mCallback?.isHasNewVersion(true)

            if (isStartUpdateVersion) {
                checkVersionRequest(packageNames)
            }
        }
    }

    /**
     * 请求要下载的安装包
     */
    private fun checkVersionRequest(packageNames: String) {
        val params = "${UpdateConfig.PROJECT_NAME}/${UpdateConfig.ROBOT_TYPE}/${UpdateConfig.BRANCH_NAME}/${UpdateConfig.PAD_MAC}/${UpdateConfig.VERSION_CODE}"
        val url = "${UpdateConfig.VERSION_UPDATE_URL}getUpdateApp/$params"
        Log.e(TAG, "开始获取下载地址 url -> $url")

        val requestBody = FormBody.Builder().add("packageNames", packageNames).build()
        val request = Request.Builder().url(url).post(requestBody).build()

        mCall = mOkHttpClient.newCall(request)

        mCall!!.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                val msg = if (call.isCanceled) {
                    "请求被取消"
                } else {
                    mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
                    "request failed -> ${StringUtils.getErrorDetail(e)}"
                }
                Log.e(TAG, "$url $msg")
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                Log.e(TAG, "获取下载路径 -> $url")
                if (response.isSuccessful) {
                    val result = response.body()!!.string()
                    val jsonResult = JSONObject.parseObject(result)
                    val statue = jsonResult.getIntValue("code")

                    if (statue == 200) {
                        val resultJson = jsonResult.getJSONObject("result")
                        var downloadPath = ""
                        if (resultJson != null) {
                            downloadPath = resultJson.getString("url")
                            zipMD5 = resultJson.getString("md5")
                        }

                        if (TextUtils.isEmpty(downloadPath) || TextUtils.isEmpty(zipMD5)) {
                            mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
                            EmailUtils.sendUpdateErrorEmail("服务器返回下载地址有误\nresult = $result")
                            Log.e(TAG, "服务器返回下载地址有误")
                        } else {
                            downloadPath = downloadPath.replace("\\\\".toRegex(), "/")
                            startDownloadZip(downloadPath)
                        }
                    } else {
                        val msg = jsonResult.getString("msg")
                        mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
                        EmailUtils.sendUpdateErrorEmail("服务器返回下载地址有误\nresult = $result")
                        Log.e(TAG, "stop update version -> \n$msg")
                    }
                } else {
                    mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
                    Log.e(TAG, "Unexpected code $response")
                }
                response.close()
            }
        })
    }

    /**
     * 请求重试拦截器
     */
    private inner class RetryInterceptor(val maxRetry: Int = 3) : Interceptor {

        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response? {
            val request = chain.request()
            var response: Response? = null
            try {
                response = chain.proceed(request)
            } catch (e: IOException) {
            }

            var count = 0
            while ((response == null || !response.isSuccessful) && count < maxRetry) {
                try {
                    Thread.sleep(3000)
                    Log.e(TAG, "${request.url()} 请求失败，开始第 ${++count} 次重试")
                    response = chain.proceed(request)
                } catch (e: InterruptedException) {

                } catch (e: IOException) {
                    if (chain.call().isCanceled || count == maxRetry) {
                        throw e
                    }
                }
            }
            return response
        }
    }

    /**
     * 开始下载
     */
    private fun startDownloadZip(result: String) {
        if (isDownloading) {
            return
        }

        val zipDir = File(UpdateConfig.ZIP_APP_PATH)
        if (!zipDir.exists()) {
            zipDir.mkdirs()
        }
        val downloadFileName = "$zipMD5.zip"

        // 删除其它文件
        zipDir.listFiles().forEach {
            // 存在已下载好的 zip 包
            if (it.name == downloadFileName && it.name.removeSuffix(".zip").equals(Md5Utils.encryptMD5File2String(it), true)) {
                Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "下载完毕，开始解压").sendToTarget()
                thread {
                    unZipAndInstall(it.absolutePath)
                }
                return
            }
            if (it.name != "$downloadFileName.tmp") {
                it.delete()
            }
        }

        val destPath = "${zipDir.absolutePath}/$downloadFileName"

        val url = "${UpdateConfig.VERSION_UPDATE_RESOURCE_URL}$result"
        Log.e(TAG, "更新包下载路径 -> $url")

        val request = DownloadRequest.Builder().url(url)
                .retryTime(5)
                .retryInterval(5, TimeUnit.SECONDS)
                .progressInterval(1, TimeUnit.SECONDS)
                .destinationFilePath(destPath)
                .downloadCallback(object : DownloadCallback() {
                    var retryCount = 0
                    override fun onStart(downloadId: Int, totalBytes: Long) {
                        Log.e(TAG, "downloadZip onStart url -> $url")
                        Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "开始下载更新包").sendToTarget()
                    }

                    override fun onRetry(downloadId: Int) {
                        Log.e(TAG, "$url 下载失败，开始第 ${++retryCount} 次重试")
                    }

                    override fun onProgress(downloadId: Int, bytesWritten: Long, totalBytes: Long) {
                        isDownloading = true
                        val progress = bytesWritten * 100 / totalBytes
                        Log.d(TAG, "download progress = $progress%")
                        Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "当前下载进度：$progress%").sendToTarget()
                    }

                    override fun onSuccess(downloadId: Int, filePath: String) {
                        Log.e(TAG, "zip download successful")
                        isDownloading = false
                        val destFile = File(filePath)
                        if (destFile.name.removeSuffix(".zip").equals(Md5Utils.encryptMD5File2String(destFile), true).not()) {
                            Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "文件MD5校验失败，开始重新下载").sendToTarget()
                            FileUtils.deleteFile(destFile)
                            startDownloadZip(result)
                        } else {
                            Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "下载完毕，开始解压").sendToTarget()
                            thread {
                                unZipAndInstall(filePath)
                            }
                        }
                    }

                    override fun onFailure(downloadId: Int, statusCode: Int, errMsg: String?) {
                        Log.e(TAG, "zip download onFailure -> $errMsg")
                        isDownloading = false
                        mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)

                    }
                }).build()

        mDownloadManager.cancelAll()
        mDownloadManager.add(request)
    }

    /**
     * 解压安装
     */
    private fun unZipAndInstall(zipPath: String) {

        // 删除老版本 apk 文件
        FileUtils.deleteDir(UpdateConfig.PATH_UPDATE_APK)

        try {
            if (ZipUtils.unZipFiles(zipPath, "${ConstUtils.PATH_SD_ROOT}/")) {

                Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "解压完毕").sendToTarget()
                Log.e(TAG, "解压完毕")

                val apkDir = File(UpdateConfig.PATH_UPDATE_APK)

                apkDir.listFiles().forEach {
                    val packageName = it.name
                    // 版本号目录或 diffApk 目录

                    it.listFiles().forEach { versionCodeFile ->
                        val dirName = versionCodeFile.name
                        val apkFile = versionCodeFile.listFiles()[0]
                        val apkFileInfo = ApkFileInfo()
                        apkFileInfo.packagename = packageName
                        // 差分文件
                        when (dirName) {
                            "diffApk" -> {
                                val oldVersionApk = File(RobotUtil.getSourceApkPath(mContext, packageName)!!)
                                val newsApk = File(apkFile.parent, "news.apk")
                                var msg = "开始合成 apk -> $packageName"
                                Log.e(TAG, msg)
                                Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, msg).sendToTarget()
                                // 合并 apk
                                val isSuccess = BsPatch.bsPatch(oldVersionApk, newsApk, apkFile)

                                if (isSuccess) {
                                    apkFileInfo.apkFile = newsApk
                                    apkFileInfos.add(apkFileInfo)
                                    msg = "$packageName 合包成功"
                                } else {
                                    msg = "$packageName 合包失败"
                                }

                                Log.e(TAG, msg)
                                Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, msg).sendToTarget()
                            }
                            "diffAi" -> {
                                Message.obtain(mHandler, UpdateActivity.VERSION_UPDATE_FINISHED, "开始更新 ai").sendToTarget()
                                // 解压 zip
                                ZipUtils.unZipFiles(apkFile, "${ConstUtils.PATH_SD_ROOT}/")
                                UpdateConfig.aiVersionCode = serverAiVersionCode.toString()
                                deleteOldVersionAiFile()
                                Message.obtain(mHandler, UpdateActivity.VERSION_UPDATE_FINISHED, "ai 更新完毕").sendToTarget()
                            }
                            else -> {
                                apkFileInfo.apkFile = apkFile
                                apkFileInfos.add(apkFileInfo)
                            }
                        }
                    }
                }

                // 只有 ai 更新
                if (apkFileInfos.size == 0) {
                    packageNames = ""
                    Message.obtain(mHandler, UpdateActivity.VERSION_UPDATE_FINISHED, "版本更新完毕").sendToTarget()
                    return
                }

                if (apkFileInfos.size > 1) {
                    // 将本程序留到最后安装
                    for (info in apkFileInfos) {
                        if (info.packagename == mContext.packageName) {
                            apkFileInfos.remove(info)
                            apkFileInfos.add(info)
                            break
                        }
                    }
                }
                val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED)
                intentFilter.addDataScheme("package")
                mContext.registerReceiver(mInstallResultReceiver, intentFilter)

                // 自身暂不安装
                if (apkFileInfos.size == 1 && apkFileInfos[0].packagename == mContext.packageName) {
                    mHandler.sendEmptyMessage(UpdateActivity.VERSION_READY)
                } else {
                    startServiceInstallApk(apkFileInfos[0])
                }
            } else {
                val msg = "unZip failure"
                mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
                Log.e(TAG, msg)
            }
        } catch (e: IOException) {
            mHandler.sendEmptyMessage(UpdateActivity.UPDATE_VERSION_FAILURE)
            e.printStackTrace()
        }

    }

    /**
     * 删除老版本 ai 文件
     */
    private fun deleteOldVersionAiFile() {
        File(UpdateConfig.PATH_DELETE_FILE).takeIf { it.exists() }?.run {
            readLines(Charset.forName("utf-8")).forEach { lineContent ->
                File(ConstUtils.PATH_SD_ROOT, "ai$lineContent").takeIf { it.exists() }?.also {
                    it.delete()
                }
            }
        }
    }

    /**
     * 启动静默安装服务安装 apk
     */
    private fun startServiceInstallApk1(apkFileInfo: ApkFileInfo) {
/*        val msg = "开始安装 ${AppUtils.getAppName(mContext, apkFileInfo)}"
        Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, msg).sendToTarget()

        val updateServiceIntent = Intent()
        updateServiceIntent.setClassName("com.uurobot.slientinstall", "com.uurobot.slientinstall.InstallService")
        updateServiceIntent.putExtra("packageName", apkFileInfo.packagename)
        updateServiceIntent.putExtra("apkFilePath", apkFileInfo.apkFile!!.absolutePath)
        mContext.startService(updateServiceIntent)
        Log.e(TAG, "start SilentInstall Service  $msg")*/

    }

    private fun startServiceInstallApk(apkFileInfo: ApkFileInfo): Unit {
        val msg = "开始安装 ${AppUtils.getAppName(mContext, apkFileInfo)}"
        Log.e(TAG, "startServiceInstallApk:   $msg")
        Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, msg).sendToTarget()
        val intent = Intent()
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        intent.action = Intent.ACTION_VIEW
        val path = Uri.fromFile(apkFileInfo.apkFile)
        intent.setDataAndType(path, "application/vnd.android.package-archive")
        mContext.startActivity(intent)
    }

    /**
     * 开始安装自身，先保证头部更新完毕
     */
    fun startInstallSelf() {
        startServiceInstallApk(apkFileInfos[0])
    }

    /**
     * apk 安装结果广播
     */
    private val mInstallResultReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val packageName = intent.data.schemeSpecificPart
            Log.e(TAG, "mInstallResultReceiver onReceive: " + packageName + "  " + apkFileInfos.size)
            Message.obtain(mHandler, UpdateActivity.UPDATE_VERSION_SHOW_INFO, "${AppUtils.getAppName(mContext, apkFileInfos[0])} 安装成功").sendToTarget()

            apkFileInfos.removeAt(0)

            when (apkFileInfos.size) {
                0 -> {
                    packageNames = ""
                    Message.obtain(mHandler, UpdateActivity.VERSION_UPDATE_FINISHED, "版本更新完毕").sendToTarget()
                    mContext.unregisterReceiver(this)
                }
                1 -> {
                    if (apkFileInfos[0].packagename == mContext.packageName) {
                        mHandler.sendEmptyMessage(UpdateActivity.VERSION_READY)
                    } else {
                        startServiceInstallApk(apkFileInfos[0])
                    }
                }
                else -> {
                    startServiceInstallApk(apkFileInfos[0])
                }
            }
        }
    }

    /**
     * 取消更新
     */
    fun cancelUpdate() {
        mCall?.cancel()
        isDownloading = false
        mDownloadManager.cancelAll()
    }

    private lateinit var mCheckNewVersionDialog: Dialog
    private lateinit var mUpdateDialog: Dialog

    /**
     * 是否更新全局弹窗询问
     *
     */
    fun showUpdateSelectDialog(intent: Intent, isAutoCheckNewVersion: Boolean = false): Dialog {
        if (this::mUpdateDialog.isInitialized && mUpdateDialog.isShowing) {
            return mUpdateDialog
        }
        mUpdateDialog = CommonDialog(mContext).hintDialog(UpdateConfig.Update_Tip, UpdateConfig.Update_Tip_Msg, object : CommonDialog.OnSelectListener {
            override fun onCancle(view: View?) {
                if (isAutoCheckNewVersion) {
                    UpdateConfig.isCancelAutoCheckNewVersion = true
                }
                mUpdateDialog.dismiss()
            }

            override fun onEnsure(view: View?) {
                mUpdateDialog.dismiss()
                mContext.startActivity(intent)
            }
        })
        return mUpdateDialog
    }

    /**
     * 版本更新检测 Dialog 显示
     * @param activity 检测版本更新的 activity
     */
    @SuppressLint("InflateParams")
    fun showCheckNewVersionDialog(activity: Activity): Dialog {
        if (this::mCheckNewVersionDialog.isInitialized && mCheckNewVersionDialog.isShowing) {
            return mCheckNewVersionDialog
        }

        val view = activity.layoutInflater.inflate(R.layout.dialog_check_new_version, null)
        mCheckNewVersionDialog = FullScreenDialog(activity, R.style.MyDialogStyle).apply {
            setContentView(view)
            setCancelable(false)
            show()
        }
        return mCheckNewVersionDialog
    }

    /**
     * 是否有版本更新的回调
     */
    abstract class VersionUpdateCallback {
        /**
         * 是否存在新版本
         * @param isHas true：有新版本，false：没有新版本
         * @param enabledUpdate true：开启了版本更新，false：没有开启更新
         */
        abstract fun isHasNewVersion(isHas: Boolean, enabledUpdate: Boolean = true)

        /**
         * 检测版本更新失败
         *
         * @param msg 失败信息
         */
        open fun onFailure(msg: String, enabledUpdate: Boolean = true) {}
    }

    companion object {

        val TAG: String = VersionUpdateUtils::class.java.simpleName

        @SuppressLint("StaticFieldLeak")
        @Volatile
        private var instance: VersionUpdateUtils? = null

        // ai 资源包名
        const val AI_PACKAGE_NAME = "com.canbot.resource.ai"

        // 服务器上 ai 版本号
        private var serverAiVersionCode: Int = 0

        fun getInstance(context: Context): VersionUpdateUtils {
            return instance ?: synchronized(this) {
                instance ?: VersionUpdateUtils(context.applicationContext).also { instance = it }
            }
        }

        /**
         * apk 安装结果广播
         */
        const val INSTALL_RESULT_ACTION = "action.install.result"
    }

}
